using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using T.FAS.Runtime.Metadata.Entity;
using T.FAS.Runtime.Action.SPI;
using T.FAS.Runtime.Base.Entity;
using T.FAS.Runtime.Metadata.Entity.DataModel;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace T.FAS.Runtime.Action.Service
{
    class ActionExecuteContextBuilder
    {
        public static ActionExecContext Build(string actionId, SPI.ActionType actionType, List<ActionParameter> parameters, bool isMatchMetadataByCode = false)
        {
            var result = new ActionExecContext();

            FASMetaData metaData = null;
            switch (actionType)
            {
                case SPI.ActionType.CDO_Create:
                case SPI.ActionType.CDO_Delete:
                case SPI.ActionType.CDO_GetByID:
                case SPI.ActionType.CDO_Update:
                case SPI.ActionType.CDO_BatchUpdate:
                case SPI.ActionType.CDO_BatchDelete:
                case SPI.ActionType.CDO_BatchCreate:
                case SPI.ActionType.CDO_BatchEditUpdate:
                    {
                        metaData = MetadataRTPService.GetInstance().GetActionByCDO(actionId, actionType, isMatchMetadataByCode);
                        if (actionType != SPI.ActionType.CDO_BatchUpdate 
                            && actionType != SPI.ActionType.CDO_BatchDelete
                            && actionType != SPI.ActionType.CDO_BatchEditUpdate 
                            && actionType != SPI.ActionType.CDO_BatchCreate)
                        {
                            actionType = SPI.ActionType.Action;
                        }
                        result.OriginalActionMetaData = MetadataRTPService.GetInstance().GetMetadataByIDOrCodeFromCache(actionId).MetaDataContent as CompositeDataObject;
                        isMatchMetadataByCode = false;
                        break;
                    }
                default:
                    break;
            }
            if (metaData == null)
                metaData = MetadataRTPService.GetInstance().GetMetadata(actionId, actionType, isMatchMetadataByCode);
            if (metaData == null)
                throw new FasException(ActionExecuteExceptionCode.ActionMetaDataNotExistError, string.Format(CommonStringClassService.ActionExecuteContextBuilder_Build2, (isMatchMetadataByCode ? CommonStringClassService.ActionExecuteContextBuilder_Build : CommonStringClassService.ActionExecuteContextBuilder_Build1), actionId));
            if (result.OriginalActionMetaData == null && actionType == SPI.ActionType.Action)
            {
                var tmpAction = metaData.MetaDataContent as ActionMetaData;
                if (tmpAction != null && tmpAction.ActionType == Metadata.Entity.ActionType.CDO && !string.IsNullOrEmpty(tmpAction.RelatedMetaDataID))
                {
                    result.OriginalActionMetaData = MetadataRTPService.GetInstance().GetMetadataByIDOrCodeFromCache(tmpAction.RelatedMetaDataID).MetaDataContent as CompositeDataObject;
                }
            }
            result.MetadataID = metaData.MetaDataID;
            result.MetadataCode = metaData.MetaDataCode;
            result.ActionType = actionType;
            result.AppID = metaData.AppID;
            result.MetaData = metaData.MetaDataContent;
            result.DependencyMetaDatas = metaData.DependencyMetadatas;
            result.DataContent = InitActionExecuteParameters(result.MetaData, actionType, parameters);
            result.Cookies = GetCookies(parameters);
            return result;
        }

        /// <summary>
        /// 创建Action执行上下文
        /// </summary>
        /// <param name="actionId"></param>
        /// <param name="actionCode"></param>
        /// <param name="parameters"></param>
        /// <param name="isMatchMetadataByCode"></param>
        /// <returns></returns>
        public static ActionExecContext Build(string actionId, string actionCode, List<ActionParameter> parameters, bool isMatchMetadataByCode = false)
        {
            var result = new ActionExecContext();
            result.MetadataID = actionId;
            result.ActionCode = actionCode;
            var plugin = ActionPluginInfoService.Instance.GetActionPluginInfoByActionCode(actionCode);
            result.ActionPlugin = plugin;
            var metaData = MetadataRTPService.GetInstance().GetFASMetadataByIdOrCode(actionId, plugin.MetaTypeID, isMatchMetadataByCode);
            result.MetaData = metaData.MetaDataContent;
            result.AppID = metaData.AppID;
            if (result.MetaData == null)
                throw new FasException(ActionExecuteExceptionCode.ActionMetaDataNotExistError, string.Format(CommonStringClassService.ActionExecuteContextBuilder_Build5, (isMatchMetadataByCode ? CommonStringClassService.ActionExecuteContextBuilder_Build : CommonStringClassService.ActionExecuteContextBuilder_Build1), actionId, plugin.MetaTypeID));
            result.DataContent = InitActionExecuteParameters(result.MetaData, parameters);
            result.Cookies = GetCookies(parameters);
            result.RemoteInvokeParameter = parameters;
            return result;
        }
        private static Dictionary<string, string> GetCookies(List<ActionParameter> parameters)
        {
            Dictionary<string, string> cookies = new Dictionary<string, string>();
#if NETSTANDARD2_1_OR_GREATER
            var fascookies = parameters.FirstOrDefault(t => "fascookies".Equals(t.ParameterName, StringComparison.OrdinalIgnoreCase));
            if (fascookies == null)
            {
                var cookie = HttpContext.Current?.Request.Cookies;
                if (cookie?.Keys.Count > 0)
                {
                    foreach (var item in cookie.Keys)
                    {
                        cookies.Add(item, cookie[item]);
                    }
                }
            }
            else if(fascookies.ParameterValue is Dictionary<string, string> keyValues)
            {
                cookies = keyValues;
            }
#endif
            return cookies;
        }
        private static Dictionary<string, object> InitActionExecuteParameters(IMetaDataContent metaData, List<ActionParameter> parameters)
        {
            var result = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
            if (parameters == null || parameters.Count <= 0)
                return result;

            foreach (var item in parameters)
            {
                if (result.ContainsKey(item.ParameterName) == false)
                    result.Add(item.ParameterName, item.ParameterValue);
            }
            return result;
        }

        /// <summary>
        /// 初始化action执行参数
        /// </summary>
        /// <param name="actionMetaData"></param>
        /// <param name="actionType"></param>
        /// <param name="parameters">前端透传的参数结构</param>
        /// <returns></returns>
        private static Dictionary<string, object> InitActionExecuteParameters(IMetaDataContent actionMetaData, SPI.ActionType actionType, List<ActionParameter> parameters)
        {
            var result = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
            if (parameters == null || parameters.Count <= 0)
                return result;

            bool isMatchParamByOrder = false;
            //支持不指定参数，默认按顺序匹配参数
            var parametersWithoutName = parameters.FindAll(x => string.IsNullOrEmpty(x.ParameterName));
            if (parametersWithoutName != null && parametersWithoutName.Count > 0)
                isMatchParamByOrder = true;

            if (!isMatchParamByOrder)
            {
                var extParam = parameters.Find(x => x.ParameterName.Equals("ExtSetting", StringComparison.OrdinalIgnoreCase));
                if (extParam != null)
                {
                    result.Add(extParam.ParameterName, extParam.ParameterValue);
                    parameters.Remove(extParam);
                }
            }

            if (actionType == SPI.ActionType.DQ)
            {
                if (isMatchParamByOrder)
                {
                    var inParams = (actionMetaData as DataQuery).DataQueryParams.FindAll(x => x.IsInputParam);
                    FillParamByOrderForDQ(result, parametersWithoutName, inParams);
                }
                else
                {
                    foreach (var item in parameters)
                    {
                        if (result.ContainsKey(item.ParameterName) == false)
                            result.Add(item.ParameterName, item.ParameterValue);
                    }
                }
            }
            else if (actionType == SPI.ActionType.Action)
            {
                var action = ActionRTPUtil.ActionMetadataValid(actionMetaData);
                var inParams = action.MethodParams.FindAll(x => x.ParamType == ParamType.In);
                AppendDynamicParamsForAdvanceSQLAction(action, inParams);
                if (isMatchParamByOrder)
                {
                    if (parametersWithoutName.Count == inParams.Count + 1)//CDO兼容不指定参数时，额外传递ExtSetting的场景
                    {
                        result["ExtSetting"] = parametersWithoutName[parametersWithoutName.Count - 1].ParameterValue;
                        parametersWithoutName.RemoveAt(parametersWithoutName.Count - 1);
                    }
                    FillParamByOrderForAction(result, parametersWithoutName, inParams);
                }
                else
                {
                    foreach (var parameter in inParams)
                    {
                        var realInParameter = parameters.FirstOrDefault(i => i.ParameterName.Equals(parameter.Code, StringComparison.OrdinalIgnoreCase));
                        if (realInParameter == null)
                        {
                            if (!parameter.IsDynamic)
                                result.Add(parameter.Name, parameter.DefaultValue);
                        }
                        else
                        {
                            object val = GetValue(realInParameter, parameter);
                            result.Add(parameter.Code, val);
                        }
                    }
                }
                //Action类型的把变量默认值追加到参数集合中
                var variables = action.MethodParams.FindAll(x => x.ParamType == ParamType.Variable);
                if (variables != null && variables.Count > 0)
                {
                    variables.ForEach(v =>
                    {
                        result[v.Code] = v.DefaultValue;
                    });
                }
            }
            else if (actionType == SPI.ActionType.CDO_NewInstance
                || actionType == SPI.ActionType.CDO_ChangeState
                || actionType == SPI.ActionType.CDO_FireEvent
                || actionType == SPI.ActionType.CDO_FireEventWithRecord
                || actionType == SPI.ActionType.CDO_BatchDelete
                || actionType == SPI.ActionType.CDO_BatchUpdate
                || actionType == SPI.ActionType.CDO_BatchCreate
                || actionType == SPI.ActionType.CDO_BatchEditUpdate)
            {
                foreach (var item in parameters)
                {
                    if (result.ContainsKey(item.ParameterName) == false)
                        result.Add(item.ParameterName, item.ParameterValue);
                }
            }
            return result;
        }
        
        private static void AppendDynamicParamsForAdvanceSQLAction(ActionMetaData action, List<ActionMetaDataMethodParam> inParams)
        {
            if (action == null || action.ActionType != Metadata.Entity.ActionType.SQL ||
                (action.ActionType == Metadata.Entity.ActionType.SQL && !action.Method.IsAdvanced))
                return;
            if (string.IsNullOrEmpty(action.Method.MethodContent))
                return;
            var content = JsonConvert.DeserializeObject<SQLActionContent>(action.Method.MethodContent);
            if (content == null || content.DynamicParams == null)
                return;
            var allDynamicQueryFilterParams = content.DynamicParams.FindAll(p => p.IsQueryFilter);
            if (allDynamicQueryFilterParams != null && allDynamicQueryFilterParams.Count > 0)
            {
                var maxSortOrder = (inParams == null || inParams.Count <= 0) ? 0 : inParams.OrderBy(p => p.SortOrder).Last().SortOrder;
                allDynamicQueryFilterParams.OrderBy(p => p.SortOrder).ToList().ForEach(p =>
                {
                    maxSortOrder++;
                    var dynamicP = p.Copy();
                    dynamicP.SortOrder = maxSortOrder;
                    inParams.Add(dynamicP);
                });
            }
        }

        /// <summary>
        /// 将入参集合按照action元数据的参数顺序处理
        /// </summary>
        /// <param name="result"></param>
        /// <param name="parametersWithoutName">action调用入参实体集合</param>
        /// <param name="inParams">元数据参数列表</param>
        /// <exception cref="FasException"></exception>
        private static void FillParamByOrderForAction(Dictionary<string, object> result, List<ActionParameter> parametersWithoutName, List<ActionMetaDataMethodParam> inParams)
        {
            if (inParams != null && inParams.Count > 0)
            {
                var inParamsOrdered = inParams.OrderBy(i => i.SortOrder).ToList();

                if (parametersWithoutName != null && parametersWithoutName.Count > 0 && !parametersWithoutName.Count.Equals(inParamsOrdered.Count()))
                {
                    throw new FasException(ActionExecuteExceptionCode.ActionExecuteParamNullError, string.Format(CommonStringClassService.ActionExecuteContextBuilder_ParameterCountMismatch, "Action"));
                }
                else
                {
                    for (int i = 0; i < inParamsOrdered.Count(); i++)
                    {
                        object val = GetValue(parametersWithoutName[i], inParamsOrdered[i]);
                        result.Add(inParamsOrdered[i].Code, val);
                    }
                }
            }
        }

        /// <summary>
        /// 获取入参值，如果为空且有默认值，走默认值
        /// </summary>
        /// <param name="inputParam"></param>
        /// <param name="invokeParamMeta"></param>
        /// <returns></returns>
        private static object GetValue(ActionParameter inputParam, ActionMetaDataMethodParam invokeParamMeta)
        {
            var val = inputParam.ParameterValue;
            //增加当输入值为空时，获取默认值逻辑
            string defaultValue = invokeParamMeta.DefaultValue;
            if (IsNullOrEmpty(val) && !string.IsNullOrEmpty(defaultValue))
            {
                DataType dataType = DataType.String;
                if (!string.IsNullOrEmpty(invokeParamMeta.ParamValueType) && Enum.TryParse(invokeParamMeta.ParamValueType, out dataType))
                {
                    val = ValidateAndCastValue(dataType, defaultValue);
                }
                else
                {
                    val = defaultValue;
                }
            }
            return val;
        }

        private static void FillParamByOrderForDQ(Dictionary<string, object> result, List<ActionParameter> parametersWithoutName, List<DataQueryParam> inParams)
        {
            if (inParams != null && inParams.Count > 0)
            {
                var inParamsOrdered = inParams.OrderBy(i => i.Sort).ToList();
                if (parametersWithoutName != null && parametersWithoutName.Count > 0 && !parametersWithoutName.Count.Equals(inParamsOrdered.Count()))
                {
                    throw new FasException(ActionExecuteExceptionCode.ActionExecuteParamNullError, string.Format(CommonStringClassService.ActionExecuteContextBuilder_ParameterCountMismatch, "DQ"));
                }
                else
                {
                    for (int i = 0; i < inParamsOrdered.Count(); i++)
                    {
                        result.Add(inParamsOrdered[i].Code, parametersWithoutName[i]?.ParameterValue);
                    }
                }
            }
        }

        private static bool IsNullOrEmpty(object val)
        {
            return val == null || val == DBNull.Value || string.IsNullOrEmpty(val.ToString());
        }

        private static object ValidateAndCastValue(DataType dataType, object inputObj)
        {
            var typer = DataTypeToolFactory.ValidAndConvertToObject(dataType);
            if (typer != null)
            {
                try
                {
                    return typer.ValidAndConvertToObject(inputObj, true);
                }
                catch
                {
                }
            }
            return inputObj;
        }
    }
}